package com.atguigu.gmall.cart.service.impl;

import com.atguigu.gmall.cart.mapper.CartInfoMapper;
import com.atguigu.gmall.cart.service.CartAsyncService;
import com.atguigu.gmall.cart.service.CartInfoService;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.model.cart.CartInfo;
import com.atguigu.gmall.model.product.SkuInfo;
import com.atguigu.gmall.product.client.ProductFeignClient;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Description:
 * @author: JC
 * @date: 2021/11/3 8:52
 */
@Service
public class CartInfoServiceImpl implements CartInfoService {

    @Autowired
    private CartInfoMapper cartInfoMapper;

    @Autowired
    private ProductFeignClient productFeignClient;

    @Autowired
    private CartAsyncService cartAsyncService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void addToCart(CartInfo cartInfo) {
        // 获取购物车的key
        String carKey = getCartKey(cartInfo.getUserId());

        if(!redisTemplate.hasKey(carKey)){
            // 加载数据库中的数据到缓存！
            loadCartCache(carKey);
        }
        CartInfo cartInfoDB = null;
        try {
            // 代码走到这缓存中一定有数据了
            cartInfoDB = (CartInfo)redisTemplate.boundHashOps(carKey).get(cartInfo.getSkuId().toString());
            // 添加购物车逻辑
            // 判断该购物表中已有 同样Sku的数据
            // 有则修改 增加数量
            // 没有就新增数据

            if(cartInfoDB == null){
                // 为空表示没数据 购物车第一次加入该商品 执行插入
                SkuInfo skuInfo = productFeignClient.getSkuInfo(cartInfo.getSkuId());
                cartInfo.setCartPrice(skuInfo.getPrice());
                cartInfo.setSkuPrice(skuInfo.getPrice());
                cartInfo.setSkuName(skuInfo.getSkuName());
                cartInfo.setImgUrl(skuInfo.getSkuDefaultImg());
                cartInfo.setCreateTime(new Timestamp(new Date().getTime()));
                cartInfo.setUpdateTime(new Timestamp(new Date().getTime()));
                cartInfo.setIsChecked(1);
                // 建议同步 不然缓存中可能没最新数据
                cartAsyncService.saveCartInfo(cartInfo);

                // 放入缓存中
                redisTemplate.opsForHash().put(carKey,cartInfo.getSkuId().toString(),cartInfo);

                // 给购物车设置过期时间
                setCartKeyExpire(carKey);
                return;
            }

            Integer skuNum = cartInfoDB.getSkuNum();
            cartInfoDB.setSkuNum(++skuNum);
            // 再次选择该商品
            cartInfoDB.setIsChecked(1);
            // 查询出商品的实时价格
            BigDecimal skuPrice = productFeignClient.getSkuPrice(cartInfoDB.getSkuId());

            // 赋值商品的实时价格
            cartInfoDB.setCartPrice(skuPrice);
            cartInfoDB.setUpdateTime(new Timestamp(new Date().getTime()));
            // 更新表数据
            cartAsyncService.updateCartInfo(cartInfoDB);
        } catch (Exception e) {
            e.printStackTrace();
            // 有异常，就不存入缓存中了
            return;
        }

        //if(cartInfoDB != null){
            // 更新完后 直接将购物车放入缓存
            redisTemplate.opsForHash().put(carKey, cartInfoDB.getSkuId().toString(),cartInfoDB);
            // 给购物车设置过期时间
            setCartKeyExpire(carKey);
        //}

    }

    @Override
    public List<CartInfo> getCartList(String userId, String userTempId) {
        List<CartInfo> cartInfoList = new ArrayList<>();

        // 查询登录时的购物车
        if(!StringUtils.isEmpty(userId)){
            // 有可能会发生合并购物车：
            List<CartInfo> carList = new ArrayList<>();
            // 获取临时购物车数据：
            if(StringUtils.isEmpty(userTempId)){
                cartInfoList = getCarList(userId);
                return cartInfoList;
            }else{
                carList = getCarList(userTempId);
            }


            if(!CollectionUtils.isEmpty(carList)){
                // 说明未登录购物车数据有值！需要进行合并操作！
               cartInfoList =  mergeToCartList(carList, userId);
                // 删除临时是购物车数据
                deleteCartList(userTempId);
            }else{
                // 只查询登录购物车数据！
                cartInfoList = getCarList(userId);
            }

        }

        // 查询临时购物车数据
        if(!StringUtils.isEmpty(userTempId)){
            cartInfoList = getCarList(userTempId);
        }

        return cartInfoList;
    }

    @Override
    public void checkCart(String userId, Integer isChecked, Long skuId) {
        // 操作数据库
        cartAsyncService.checkCart(userId,isChecked,skuId);
        // 同步redis
        String cartKey = getCartKey(userId);
        CartInfo cartInfo =(CartInfo) redisTemplate.opsForHash().get(cartKey, skuId.toString());
        cartInfo.setIsChecked(isChecked);
        if(cartInfo != null){
            redisTemplate.opsForHash().put(cartKey,skuId.toString(), cartInfo);

            // 可以考虑是否需要设置过期时间
        }
    }

    @Override
    public void deleteCart(Long skuId, String userId) {
        // 删除数据库
        cartAsyncService.deleteCartInfo(userId,skuId);
        // 删除缓存
        String cartKey = getCartKey(userId);
        Boolean result = redisTemplate.boundHashOps(cartKey).hasKey(skuId.toString());
        if(result){
            redisTemplate.opsForHash().delete(cartKey, skuId.toString());
        }

    }

    @Override
    public List<CartInfo> getCartCheckedList(String userId) {

        // 数据从缓存中取出 还是数据库中取出？
        String cartKey = getCartKey(userId);

        List<CartInfo> cartInfoList =(List<CartInfo>) redisTemplate.opsForHash().values(cartKey);
        List<CartInfo> collect = cartInfoList.stream()
                .filter(cartInfo -> cartInfo.getIsChecked().intValue() == 1).collect(Collectors.toList());


//        QueryWrapper<CartInfo> cartInfoQueryWrapper = new QueryWrapper<>();
//        cartInfoQueryWrapper.eq("user_id",userId)
//                .eq("is_checked",1);

        return collect;
    }


    /**
     * 删除临时购物车
     * @param userTempId
     */
    private void deleteCartList(String userTempId) {
        // 删除购物车，redis
        String cartKey = getCartKey(userTempId);

        if(redisTemplate.hasKey(cartKey)){
            Boolean delete = redisTemplate.delete(cartKey);
        }

        CartInfo cartInfo = new CartInfo();
        cartInfo.setUserId(userTempId);
        cartAsyncService.deleteCartInfo(cartInfo);

    }

    /**
     * 合并购物车
     * @param carList
     * @param userId
     * @return
     */
    private List<CartInfo> mergeToCartList(List<CartInfo> carList, String userId) {
        // 1.根据userId 获取到登录用户的购物车集合数据
        // 2.做合并处理？处理条件： skuId 相同 数量相加；
        List<CartInfo> cartInfoLoginList = getCarList(userId);
        // 第一种方案： 双重for 遍历： 根据skuId 是否相同
        // 第二种方案： 使用map做包含 cartInfoLoginList 变成map 集合 key 为skuId
        Map<Long, CartInfo> infoMap = cartInfoLoginList.stream().collect(Collectors.toMap(CartInfo::getSkuId, o -> o));

        carList.forEach(cartInfo -> {

            Long skuId = cartInfo.getSkuId();
            boolean containsKey = infoMap.containsKey(skuId);

            if(!containsKey){
                // 但是临时购物车数据的userId 是类似于一个uuid的东西
                cartInfo.setUserId(userId);
                cartInfo.setUpdateTime(new Timestamp(new Date().getTime()));
                cartInfoMapper.insert(cartInfo);

            }else{
                CartInfo info = infoMap.get(skuId);
                cartInfo.setUpdateTime(new Timestamp(new Date().getTime()));
                // 判断未登录购物车的选中状态：
                // 以勾选为基准
                if(cartInfo.getIsChecked().intValue() == 1){
                    info.setIsChecked(1);
                }
                Integer skuNum =  info.getSkuNum() + cartInfo.getSkuNum();
                info.setSkuNum(skuNum);
                // 可能从缓存中取出的Id为空  建议同步执行
                cartInfoMapper.update(info, new QueryWrapper<CartInfo>()
                        .eq("user_id", cartInfo.getUserId())
                        .eq("sku_id", cartInfo.getSkuId()));
            }

        });

        // 最终返回合并数据
        List<CartInfo> cartInfoList = loadCartCache(userId);

        return cartInfoList;
    }


    private List<CartInfo> getCarList(String userId) {

        // 获取购物车的key
        String cartKey = getCartKey(userId);
        List<CartInfo> cartInfoList = redisTemplate.opsForHash().values(cartKey);

        if(CollectionUtils.isEmpty(cartInfoList)){
            // 缓存中没有数据（一般来说过期时间到了） ，从数据库中查询
            cartInfoList = loadCartCache(userId);
            // 返回数据
            // return cartInfoList;
        }

        // 按修改时间 进行排序
        List<CartInfo> collect = cartInfoList.stream().sorted(Comparator.comparing(CartInfo::getUpdateTime)).collect(Collectors.toList());


        return collect;
    }

    /**
     * 根据用户Id 查询数据并放入缓存！
     * @param userId
     * @return
     */
    public List<CartInfo> loadCartCache(String userId) {
        // 因为购物车表中没有价格， 实时价格要去查询，所以封装CartInfo最后记得加上价格
        QueryWrapper<CartInfo> infoQueryWrapper = new QueryWrapper<>();
        infoQueryWrapper.eq("user_id",userId);
        List<CartInfo> list = cartInfoMapper.selectList(infoQueryWrapper);

        if(CollectionUtils.isEmpty(list)){
            // 数据库中无数据,直接返回
            return list;
        }

        // 放入缓存中
        String cartKey = getCartKey(userId);
        // 循环遍历当前购物车集合，放入缓存，同时将skuPrice 赋值
        List<CartInfo> cartInfoList = list.stream().map(cartInfo -> {
            // 查询价格
            BigDecimal skuPrice = productFeignClient.getSkuPrice(cartInfo.getSkuId());
            // 实时出来的价格赋值
            cartInfo.setSkuPrice(skuPrice);
            // 放入缓存中
            // redisTemplate.opsForHash().put(cartKey, cartInfo.getSkuId(), cartInfo);
            return cartInfo;
        }).collect(Collectors.toList());

        Map<String, CartInfo> infoMap = cartInfoList.stream().collect(Collectors.toMap(o -> o.getSkuId().toString(), o -> o));
        // hmset 一次写入多条数据
        redisTemplate.opsForHash().putAll(cartKey,infoMap);
        // 设置过期时间
        setCartKeyExpire(cartKey);

        return cartInfoList;
    }

    /**
     * 获取购物车的key
     * @param userId
     * @return
     */
    private String getCartKey(String userId) {
        return RedisConst.USER_KEY_PREFIX +userId+ RedisConst.USER_CART_KEY_SUFFIX;
    }

    /**
     * 设置存活时间
     * @param carKey
     */
    private void setCartKeyExpire(String carKey) {
        redisTemplate.expire(carKey, RedisConst.USER_CART_EXPIRE, TimeUnit.SECONDS);
    }
}
